//
// This file contains a 'Sample Driver' and is licensed as such
// under the terms of your license agreement with Intel or your
// vendor.  This file may be modified by the user, subject to
// the additional terms of the license agreement
//
/** @file

  Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
  This software and associated documentation (if any) is furnished
  under a license and may only be used or copied in accordance
  with the terms of the license. Except as permitted by such
  license, no part of this software or documentation may be
  reproduced, stored in a retrieval system, or transmitted in any
  form or by any means without the express written consent of
  Intel Corporation.

**/

#include <PiSmm.h>
#include <Protocol/SmmVariable.h>
#include <Protocol/VariableLock.h>
#include <Library/DebugLib.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/PrintLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/SmmServicesTableLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/SmmServicesTableLib.h>
//
// RPPO-KBL-0085: RoyalParkOverrideBegin
//
//#include <Library/PlatformPasswordLib.h>
//
// RPPO-KBL-0085: RoyalParkOverrideEnd
//
#include "KeyService.h"
#include "UserAuthenticationGuid.h"

EFI_GUID gAdminAuthenticationGuid = ADMIN_AUTHENTICATION_GUID;

EFI_SMM_VARIABLE_PROTOCOL         *mSmmVariable;

UINTN  mAdminPasswordTryCount;

/**
  Verify if the password is correct.

  @param[in]  Password               The user input password.
  @param[in]  PasswordSize           The size of Password in byte.
  @param[in]  UserPasswordVarStruct  The storage of password in variable.

  @retval EFI_SUCCESS              The password is correct.
  @retval EFI_SECURITY_VIOLATION   The password is incorrect.
**/
EFI_STATUS
VerifyPassword (
  IN CHAR8                          *Password,
  IN UINTN                          PasswordSize,
  IN USER_PASSWORD_VAR_STRUCT       *UserPasswordVarStruct
  )
{
  BOOLEAN  HashOk;
  UINT8    HashData[PASSWORD_HASH_SIZE];

//
// RPPO-KBL-0085: RoyalParkOverrideBegin
//
  HashOk = KeyLibGenerateSha256Hash (
             HASH_TYPE_SHA256,
             (UINT8 *)Password,
             PasswordSize,
             UserPasswordVarStruct->PasswordSalt,
             sizeof(UserPasswordVarStruct->PasswordSalt),
             HashData,
             sizeof(HashData)
             );
//
// RPPO-KBL-0085: RoyalParkOverrideEnd
//			 
  if (!HashOk) {
    return EFI_DEVICE_ERROR;
  }
  if (KeyLibSlowCompareMem (UserPasswordVarStruct->PasswordHash, HashData, PASSWORD_HASH_SIZE) == 0) {
    return EFI_SUCCESS;
  } else {
    return EFI_SECURITY_VIOLATION;
  }
}

/**
  Get hash data of password from non-volatile variable region.

  @param[in]   UserGuid               The user GUID of the password variable.
  @param[in]   Index                  The index of the password.
                                      0 means current password.
                                      Non-0 means the password history.
  @param[out]  UserPasswordVarStruct  The storage of password in variable.

  @retval EFI_SUCCESS             The password hash is returned successfully.
  @retval EFI_NOT_FOUND           The password hash is not found.
**/
EFI_STATUS
GetPasswordHashFromVariable (
  IN  EFI_GUID                       *UserGuid,
  IN  UINTN                          Index,
  OUT USER_PASSWORD_VAR_STRUCT       *UserPasswordVarStruct
  )
{
  EFI_STATUS                        Status;
  UINTN                             DataSize;
  CHAR16                            PasswordName[sizeof(USER_AUTHENTICATION_VAR_NAME)/sizeof(CHAR16) + 4];

  if (Index != 0) {
    UnicodeSPrint (PasswordName, sizeof (PasswordName), L"%s%04x", USER_AUTHENTICATION_VAR_NAME, Index);
  } else {
    UnicodeSPrint (PasswordName, sizeof (PasswordName), L"%s", USER_AUTHENTICATION_VAR_NAME);
  }

  DataSize = sizeof(*UserPasswordVarStruct);
  Status = mSmmVariable->SmmGetVariable (
                           PasswordName,
                           UserGuid,
                           NULL,
                           &DataSize,
                           UserPasswordVarStruct
                           );
  if (EFI_ERROR(Status)) {
    return Status;
  }

  return Status;
}

/**
  Save password hash data to non-volatile variable region.

  @param[in]   UserGuid               The user GUID of the password variable.
  @param[in]   UserPasswordVarStruct  The storage of password in variable.

  @retval EFI_SUCCESS             The password hash is saved successfully.
  @retval EFI_OUT_OF_RESOURCES    Insufficient resources to save the password hash.
**/
EFI_STATUS
SavePasswordHashToVariable (
  IN EFI_GUID                       *UserGuid,
  IN USER_PASSWORD_VAR_STRUCT       *UserPasswordVarStruct
  )
{
  EFI_STATUS                        Status;

  if (UserPasswordVarStruct == NULL) {
    Status = mSmmVariable->SmmSetVariable (
                             USER_AUTHENTICATION_VAR_NAME,
                             UserGuid,
                             EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                             0,
                             NULL
                             );
  } else {
    Status = mSmmVariable->SmmSetVariable (
                             USER_AUTHENTICATION_VAR_NAME,
                             UserGuid,
                             EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                             sizeof(*UserPasswordVarStruct),
                             UserPasswordVarStruct
                             );
  }
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "SavePasswordHashToVariable fails with %r\n", Status));
  }

  return Status;
}

/**
  Save old password hash data to non-volatile variable region as history.

  The number of password history variable is limited.
  If all the password history variables are used, the new password history
  will override the oldest one.

  @param[in]   UserGuid               The user GUID of the password variable.
  @param[in]   UserPasswordVarStruct  The storage of password in variable.

  @retval EFI_SUCCESS             The password hash is saved successfully.
  @retval EFI_OUT_OF_RESOURCES    Insufficient resources to save the password hash.
**/
EFI_STATUS
SaveOldPasswordToHistory (
  IN EFI_GUID                       *UserGuid,
  IN USER_PASSWORD_VAR_STRUCT       *UserPasswordVarStruct
  )
{
  EFI_STATUS                        Status;
  UINTN                             DataSize;
  UINT32                            LastIndex;
  CHAR16                            PasswordName[sizeof(USER_AUTHENTICATION_VAR_NAME)/sizeof(CHAR16) + 4];

  DEBUG ((DEBUG_INFO, "SaveOldPasswordToHistory\n"));

  DataSize = sizeof(LastIndex);
  Status = mSmmVariable->SmmGetVariable (
                           USER_AUTHENTICATION_HISTORY_LAST_VAR_NAME,
                           UserGuid,
                           NULL,
                           &DataSize,
                           &LastIndex
                           );
  if (EFI_ERROR(Status)) {
    LastIndex = 0;
  }
  if (LastIndex >= PASSWORD_HISTORY_CHECK_COUNT) {
    LastIndex = 0;
  }

  LastIndex ++;
  UnicodeSPrint (PasswordName, sizeof (PasswordName), L"%s%04x", USER_AUTHENTICATION_VAR_NAME, LastIndex);


  Status = mSmmVariable->SmmSetVariable (
                           PasswordName,
                           UserGuid,
                           EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                           sizeof(*UserPasswordVarStruct),
                           UserPasswordVarStruct
                           );
  DEBUG ((DEBUG_INFO, "  -- to %s, %r\n", PasswordName, Status));
  if (!EFI_ERROR(Status)) {
    Status = mSmmVariable->SmmSetVariable (
                             USER_AUTHENTICATION_HISTORY_LAST_VAR_NAME,
                             UserGuid,
                             EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                             sizeof(LastIndex),
                             &LastIndex
                             );
    DEBUG ((DEBUG_INFO, " LastIndex - 0x%04x, %r\n", LastIndex, Status));
  }

  return Status;
}

/**
  Calculate password hash data and save it to non-volatile variable region.

  @param[in]  UserGuid               The user GUID of the password variable.
  @param[in]  Password               The user input password.
                                     NULL means delete the password variable.
  @param[in]  PasswordSize           The size of Password in byte.

  @retval EFI_SUCCESS             The password hash is calculated and saved.
  @retval EFI_OUT_OF_RESOURCES    Insufficient resources to save the password hash.
**/
EFI_STATUS
SavePasswordToVariable (
  IN  EFI_GUID                      *UserGuid,
  IN  CHAR8                         *Password,  OPTIONAL
  IN  UINTN                         PasswordSize
  )
{
//
// RPPO-KBL-0085: RoyalParkOverrideBegin
//
  EFI_STATUS                        Status = EFI_SUCCESS;
//
// RPPO-KBL-0085: RoyalParkOverrideEnd
//  
  USER_PASSWORD_VAR_STRUCT          UserPasswordVarStruct;
  BOOLEAN                           HashOk;

  //
  // If password is NULL, it means we want to clean password field saved in variable region.
  //
  if (Password != NULL) {
    KeyLibGenerateSalt (UserPasswordVarStruct.PasswordSalt, sizeof(UserPasswordVarStruct.PasswordSalt));
//
// RPPO-KBL-0085: RoyalParkOverrideBegin
//	
    HashOk = KeyLibGenerateSha256Hash (
               HASH_TYPE_SHA256,
               (UINT8 *)Password,
               PasswordSize,
               UserPasswordVarStruct.PasswordSalt,
               sizeof(UserPasswordVarStruct.PasswordSalt),
               UserPasswordVarStruct.PasswordHash,
               sizeof(UserPasswordVarStruct.PasswordHash)	
	);
//
// RPPO-KBL-0085: RoyalParkOverrideEnd
//	
    if (!HashOk) {
      return EFI_DEVICE_ERROR;
    }
    Status = SavePasswordHashToVariable (UserGuid, &UserPasswordVarStruct);
	
    //
    // Save Password data to history variable
    //	
    if (!EFI_ERROR(Status)) {
//
// RPPO-KBL-0085: RoyalParkOverrideBegin
//	
      Status = SaveOldPasswordToHistory (UserGuid, &UserPasswordVarStruct);
//
// RPPO-KBL-0085: RoyalParkOverrideEnd
//	  
     }   
  } else {
    Status = SavePasswordHashToVariable (UserGuid, NULL);
  }

  return Status;
}

/**
  Verify the password.
  If the password variable does not exist, it passes the verification.
  If the password variable exists, it does verification based upon password variable.

  @param[in]  UserGuid               The user GUID of the password variable.
  @param[in]  Password               The user input password.
  @param[in]  PasswordSize           The size of Password in byte.

  @retval TRUE    The verification passes.
  @retval FALSE   The verification fails.
**/
BOOLEAN
IsPasswordVerified (
  IN EFI_GUID                       *UserGuid,
  IN CHAR8                          *Password,
  IN UINTN                          PasswordSize
  )
{
  USER_PASSWORD_VAR_STRUCT          UserPasswordVarStruct;
  EFI_STATUS                        Status;
  UINTN                             *PasswordTryCount;

  ASSERT (CompareGuid (UserGuid, &gAdminAuthenticationGuid));

  PasswordTryCount = &mAdminPasswordTryCount;

  Status = GetPasswordHashFromVariable (UserGuid, 0, &UserPasswordVarStruct);
  if (EFI_ERROR(Status)) {
    return TRUE;
  }

  //
  // Old password exists
  //
  Status = VerifyPassword (Password, PasswordSize, &UserPasswordVarStruct);
  if (EFI_ERROR(Status)) {
    if ((AsciiStrnLenS(Password, sizeof(Password))) != 0) {
      *PasswordTryCount = *PasswordTryCount + 1;
    }
    return FALSE;
  }

  return TRUE;
}

/**
  Return if the password is set.

  @param[in]  UserGuid               The user GUID of the password variable.

  @retval TRUE    The password is set.
  @retval FALSE   The password is not set.
**/
BOOLEAN
IsPasswordSet (
  IN EFI_GUID                       *UserGuid
  )
{
  USER_PASSWORD_VAR_STRUCT          UserPasswordVarStruct;
  EFI_STATUS                        Status;

  Status = GetPasswordHashFromVariable(UserGuid, 0, &UserPasswordVarStruct);
  if (EFI_ERROR(Status)) {
    return FALSE;
  }
  return TRUE;
}

/**
  Return if the password is strong.
  Criteria:
  1) length >= PASSWORD_MIN_SIZE
  2) include lower case, upper case, number, symbol.

  @param[in]  Password               The user input password.
  @param[in]  PasswordSize           The size of Password in byte.

  @retval TRUE    The password is strong.
  @retval FALSE   The password is weak.
**/
BOOLEAN
IsPasswordStrong (
  IN CHAR8   *Password,
  IN UINTN   PasswordSize
  )
{
  UINTN   Index;
  BOOLEAN HasLowerCase;
  BOOLEAN HasUpperCase;
  BOOLEAN HasNumber;
  BOOLEAN HasSymbol;

  if (PasswordSize < PASSWORD_MIN_SIZE) {
    return FALSE;
  }

  HasLowerCase = FALSE;
  HasUpperCase = FALSE;
  HasNumber = FALSE;
  HasSymbol = FALSE;
  for (Index = 0; Index < PasswordSize - 1; Index++) {
    if (Password[Index] >= 'a' && Password[Index] <= 'z') {
      HasLowerCase = TRUE;
    } else if (Password[Index] >= 'A' && Password[Index] <= 'Z') {
      HasUpperCase = TRUE;
    } else if (Password[Index] >= '0' && Password[Index] <= '9') {
      HasNumber = TRUE;
    } else {
      HasSymbol = TRUE;
    }
  }
  if ((!HasLowerCase) || (!HasUpperCase) || (!HasNumber) || (!HasSymbol)) {
    return FALSE;
  }
  return TRUE;
}

/**
  Return if the password is set before in PASSWORD_HISTORY_CHECK_COUNT.

  @param[in]  UserGuid               The user GUID of the password variable.
  @param[in]  Password               The user input password.
  @param[in]  PasswordSize           The size of Password in byte.

  @retval TRUE    The password is set before.
  @retval FALSE   The password is not set before.
**/
BOOLEAN
IsPasswordInHistory (
  IN EFI_GUID                       *UserGuid,
  IN CHAR8                          *Password,
  IN UINTN                          PasswordSize
  )
{
  EFI_STATUS                     Status;
  USER_PASSWORD_VAR_STRUCT       UserPasswordVarStruct;
  UINTN                          Index;

  for (Index = 1; Index <= PASSWORD_HISTORY_CHECK_COUNT; Index++) {
    Status = GetPasswordHashFromVariable (UserGuid, Index, &UserPasswordVarStruct);
    if (!EFI_ERROR(Status)) {
      Status = VerifyPassword (Password, PasswordSize, &UserPasswordVarStruct);
      if (!EFI_ERROR(Status)) {
        return TRUE;
      }
    }
  }

  return FALSE;
}

/**
  Communication service SMI Handler entry.

  This SMI handler provides services for password management.

  @param[in]     DispatchHandle  The unique handle assigned to this handler by SmiHandlerRegister().
  @param[in]     RegisterContext Points to an optional handler context which was specified when the
                                 handler was registered.
  @param[in, out] CommBuffer     A pointer to a collection of data in memory that will
                                 be conveyed from a non-SMM environment into an SMM environment.
  @param[in, out] CommBufferSize The size of the CommBuffer.

  @retval EFI_SUCCESS                         The interrupt was handled and quiesced. No other handlers
                                              should still be called.
  @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED  The interrupt has been quiesced but other handlers should
                                              still be called.
  @retval EFI_WARN_INTERRUPT_SOURCE_PENDING   The interrupt is still pending and other handlers should still
                                              be called.
  @retval EFI_INTERRUPT_PENDING               The interrupt could not be quiesced.
**/
EFI_STATUS
EFIAPI
SmmPasswordHandler (
  IN     EFI_HANDLE                 DispatchHandle,
  IN     CONST VOID                 *RegisterContext,
  IN OUT VOID                       *CommBuffer,
  IN OUT UINTN                      *CommBufferSize
  )
{
  EFI_STATUS                                Status;
  SMM_PASSWORD_COMMUNICATE_HEADER           *SmmFunctionHeader;
  UINTN                                     CommBufferPayloadSize;
  UINTN                                     TempCommBufferSize;
  SMM_PASSWORD_COMMUNICATE_SET_PASSWORD     SmmCommunicateSetPassword;
  SMM_PASSWORD_COMMUNICATE_VERIFY_PASSWORD  SmmCommunicateVerifyPassword;
  UINTN                                     PasswordLen;
  EFI_GUID                                  UserGuid;
  UINTN                                     *PasswordTryCount;

  //
  // If input is invalid, stop processing this SMI
  //
  if (CommBuffer == NULL || CommBufferSize == NULL) {
    return EFI_SUCCESS;
  }

  TempCommBufferSize = *CommBufferSize;
  PasswordLen = 0;

  if (TempCommBufferSize < sizeof (SMM_PASSWORD_COMMUNICATE_HEADER)) {
    DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: SMM communication buffer size invalid!\n"));
    return EFI_SUCCESS;
  }

  CommBufferPayloadSize = TempCommBufferSize - sizeof (SMM_PASSWORD_COMMUNICATE_HEADER);

  Status   = EFI_SUCCESS;
  SmmFunctionHeader = (SMM_PASSWORD_COMMUNICATE_HEADER *)CommBuffer;
  CopyGuid (&UserGuid, &SmmFunctionHeader->UserGuid);

  if (CompareGuid (&UserGuid, &gAdminAuthenticationGuid)) {
    PasswordTryCount = &mAdminPasswordTryCount;
  } else {
    DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: Invalid UserGuid\n"));
    PasswordTryCount = NULL;
    Status = EFI_INVALID_PARAMETER;
    goto EXIT;
  }

  switch (SmmFunctionHeader->Function) {
  case SMM_PASSWORD_FUNCTION_IS_PASSWORD_SET:
    PasswordTryCount = NULL;
    if (CommBufferPayloadSize != 0) {
      DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: IS_PASSWORD_SET payload buffer invalid!\n"));
      Status = EFI_INVALID_PARAMETER;
      goto EXIT;
    }
    if (IsPasswordSet(&UserGuid)) {
      Status = EFI_SUCCESS;
    } else {
      Status = EFI_NOT_FOUND;
    }
    break;
  case SMM_PASSWORD_FUNCTION_SET_PASSWORD:
    if (*PasswordTryCount >= PASSWORD_MAX_TRY_COUNT) {
      DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: SET_PASSWORD try count reach!\n"));
      PasswordTryCount = NULL;
      Status = EFI_INVALID_PARAMETER;
      goto EXIT;
    }
    if (CommBufferPayloadSize != sizeof(SMM_PASSWORD_COMMUNICATE_SET_PASSWORD)) {
      DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: SET_PASSWORD payload buffer invalid!\n"));
      Status = EFI_INVALID_PARAMETER;
      goto EXIT;
    }
    CopyMem (&SmmCommunicateSetPassword, SmmFunctionHeader + 1, sizeof(SmmCommunicateSetPassword));

    PasswordLen = AsciiStrnLenS(SmmCommunicateSetPassword.OldPassword, sizeof(SmmCommunicateSetPassword.OldPassword));
    if (PasswordLen == sizeof(SmmCommunicateSetPassword.OldPassword)) {
      DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: OldPassword invalid!\n"));
      Status = EFI_INVALID_PARAMETER;
      goto EXIT;
    }

    if (!IsPasswordVerified (&UserGuid, SmmCommunicateSetPassword.OldPassword, PasswordLen + 1)) {
      DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: PasswordVerify - FAIL\n"));
      Status = EFI_SECURITY_VIOLATION;
      goto EXIT;
    }

    PasswordLen = AsciiStrnLenS(SmmCommunicateSetPassword.NewPassword, sizeof(SmmCommunicateSetPassword.NewPassword));
    if (PasswordLen == sizeof(SmmCommunicateSetPassword.NewPassword)) {
      DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: NewPassword invalid!\n"));
      Status = EFI_INVALID_PARAMETER;
      goto EXIT;
    }
    if (PasswordLen != 0 && !IsPasswordStrong (SmmCommunicateSetPassword.NewPassword, PasswordLen + 1)) {
      DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: NewPassword too weak!\n"));
      Status = EFI_UNSUPPORTED;
      goto EXIT;
    }
    if (PasswordLen != 0 && IsPasswordInHistory (&UserGuid, SmmCommunicateSetPassword.NewPassword, PasswordLen + 1)) {
      DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: NewPassword in history!\n"));
      Status = EFI_ALREADY_STARTED;
      goto EXIT;
    }

    if (PasswordLen == 0) {
      Status = SavePasswordToVariable (&UserGuid, NULL, 0);
    } else {
      Status = SavePasswordToVariable (&UserGuid, SmmCommunicateSetPassword.NewPassword, PasswordLen + 1);
    }
    break;

  case SMM_PASSWORD_FUNCTION_VERIFY_PASSWORD:
    if (*PasswordTryCount >= PASSWORD_MAX_TRY_COUNT) {
      DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: VERIFY_PASSWORD try count reach!\n"));
      PasswordTryCount = NULL;
      Status = EFI_INVALID_PARAMETER;
      goto EXIT;
    }
    if (CommBufferPayloadSize != sizeof(SMM_PASSWORD_COMMUNICATE_VERIFY_PASSWORD)) {
      DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: VERIFY_PASSWORD payload buffer invalid!\n"));
      Status = EFI_INVALID_PARAMETER;
      goto EXIT;
    }
    CopyMem (&SmmCommunicateVerifyPassword, SmmFunctionHeader + 1, sizeof(SmmCommunicateVerifyPassword));

    PasswordLen = AsciiStrnLenS(SmmCommunicateVerifyPassword.Password, sizeof(SmmCommunicateVerifyPassword.Password));
    if (PasswordLen == sizeof(SmmCommunicateVerifyPassword.Password)) {
      DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: Password invalid!\n"));
      Status = EFI_INVALID_PARAMETER;
      goto EXIT;
    }
    if (!IsPasswordVerified (&UserGuid, SmmCommunicateVerifyPassword.Password, PasswordLen + 1)) {
      DEBUG ((DEBUG_ERROR, "SmmPasswordHandler: PasswordVerify - FAIL\n"));
      Status = EFI_SECURITY_VIOLATION;
      goto EXIT;
    }
    Status = EFI_SUCCESS;
    break;

  default:
    PasswordTryCount = NULL;
    Status = EFI_UNSUPPORTED;
    break;
  }

EXIT:
  if (PasswordTryCount != NULL) {
    if (Status == EFI_SUCCESS) {
      *PasswordTryCount = 0;
    }
  }
  SmmFunctionHeader->ReturnStatus = Status;

  return EFI_SUCCESS;
}

/**
  Main entry for this driver.

  @param ImageHandle     Image handle this driver.
  @param SystemTable     Pointer to SystemTable.

  @retval EFI_SUCESS     This function always complete successfully.

**/
EFI_STATUS
EFIAPI
PasswordSmmInit (
  IN EFI_HANDLE                         ImageHandle,
  IN EFI_SYSTEM_TABLE                   *SystemTable
  )
{
  EFI_STATUS                            Status;
  EFI_HANDLE                            SmmHandle;
  EDKII_VARIABLE_LOCK_PROTOCOL          *VariableLock;
  CHAR16                                PasswordHistoryName[sizeof(USER_AUTHENTICATION_VAR_NAME)/sizeof(CHAR16) + 4];
  UINTN                                 Index;

  ASSERT (PASSWORD_HASH_SIZE == SHA256_DIGEST_SIZE);
  ASSERT (PASSWORD_HISTORY_CHECK_COUNT < 0xFFFF);

  Status = gSmst->SmmLocateProtocol (&gEfiSmmVariableProtocolGuid, NULL, (VOID**)&mSmmVariable);
  ASSERT_EFI_ERROR (Status);

  SmmHandle          = NULL;

  //
  // Make "HddPassword" varible read-only for DXE driver for security concern.
  //
  Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock);
  if (!EFI_ERROR (Status)) {
    Status = VariableLock->RequestToLock (VariableLock, USER_AUTHENTICATION_VAR_NAME, &gAdminAuthenticationGuid);
    ASSERT_EFI_ERROR (Status);

    for (Index = 1; Index <= PASSWORD_HISTORY_CHECK_COUNT; Index++) {
      UnicodeSPrint (PasswordHistoryName, sizeof (PasswordHistoryName), L"%s%04x", USER_AUTHENTICATION_VAR_NAME, Index);
      Status = VariableLock->RequestToLock (VariableLock, PasswordHistoryName, &gAdminAuthenticationGuid);
      ASSERT_EFI_ERROR (Status);
    }
    Status = VariableLock->RequestToLock (VariableLock, USER_AUTHENTICATION_HISTORY_LAST_VAR_NAME, &gAdminAuthenticationGuid);
    ASSERT_EFI_ERROR (Status);
  }

  SmmHandle = NULL;
  Status    = gSmst->SmiHandlerRegister (SmmPasswordHandler, &gAdminAuthenticationGuid, &SmmHandle);
  ASSERT_EFI_ERROR (Status);
  if (EFI_ERROR (Status)) {
    Status = EFI_OUT_OF_RESOURCES;
    goto EXIT;
  }

//
// RPPO-KBL-0085: RoyalParkOverrideBegin
//
  /*
  if (IsPasswordCleared()) {
    DEBUG ((DEBUG_INFO, "IsPasswordCleared\n"));
    SavePasswordToVariable (&gAdminAuthenticationGuid, NULL, 0);
  }
  */
//
// RPPO-KBL-0085: RoyalParkOverrideEnd
//
  return EFI_SUCCESS;

EXIT:
  if (SmmHandle != NULL) {
    gSmst->SmiHandlerUnRegister (SmmHandle);
  }

  return Status;
}

